<!DOCTYPE html>
<html>
    <head>
        <title>Verify MediaKeySession.keyStatuses</title>
        <script src="encrypted-media-utils.js"></script>
        <script src="../../resources/testharness.js"></script>
        <script src="../../resources/testharnessreport.js"></script>
    </head>
    <body>
        <script>
            async_test(function(test)
            {
                var mediaKeySession;
                var initDataType;
                var initData;
                var closed = false;

                // Even though key ids are uint8, using printable values so that
                // they can be verified easily.
                var key1String = '123';
                var key2String = '4567890';
                var key1 = stringToUint8Array(key1String);
                var key2 = stringToUint8Array(key2String);
                var rawKey1 = new Uint8Array([0xeb, 0xdd, 0x62, 0xf1, 0x68, 0x14, 0xd2, 0x7b,
                                              0x68, 0xef, 0x12, 0x2a, 0xfc, 0xe4, 0xae, 0x3c]);
                var rawKey2 = new Uint8Array([0x3c, 0xae, 0xe4, 0xfc, 0x2a, 0x12, 0xef, 0x68,
                                              0x7b, 0xd2, 0x14, 0x68, 0xf1, 0x62, 0xdd, 0xeb]);

                function processMessage(event)
                {
                    // No keys added yet.
                    assert_equals(mediaKeySession.keyStatuses.size, 0);

                    waitForEventAndRunStep('keystatuseschange', mediaKeySession, processKeyStatusesChange, test);

                    var jwkSet = stringToUint8Array(createJWKSet(createJWK(key1, rawKey1), createJWK(key2, rawKey2)));
                    mediaKeySession.update(jwkSet).catch(function(error) {
                        forceTestFailureFromPromise(test, error);
                    });
                }

                function checkKeyStatusFor2Keys()
                {
                    // Two keys added, so both should show up in |keyStatuses|.
                    assert_equals(mediaKeySession.keyStatuses.size, 2);
                    dumpKeyStatuses(mediaKeySession.keyStatuses);

                    // Check |keyStatuses| for 2 entries.
                    var result = [];
                    for (let [keyId, status] of mediaKeySession.keyStatuses) {
                        result.push({ key: arrayBufferAsString(keyId), value: status });
                    }
                    assert_object_equals(result,
                                         [{ key: key1String, value: 'usable'}, { key: key2String, value: 'usable'}],
                                         'keyStatuses fails');

                    // |keyStatuses| must contain both keys.
                    result = [];
                    for (var key of mediaKeySession.keyStatuses.keys()) {
                        result.push(arrayBufferAsString(key));
                    }
                    assert_array_equals(result,
                                        [key1String, key2String],
                                        'keyStatuses.keys() fails');

                    // Both values in |mediaKeySession| should be 'usable'.
                    result = [];
                    for (var value of mediaKeySession.keyStatuses.values()) {
                        result.push(value);
                    }
                    assert_array_equals(result,
                                        ['usable', 'usable'],
                                        'keyStatuses.values() fails');

                    // Check |keyStatuses.entries()|.
                    result = [];
                    for (var entry of mediaKeySession.keyStatuses.entries()) {
                        result.push({ key: arrayBufferAsString(entry[0]), value: entry[1] });
                    }
                    assert_object_equals(result,
                                         [{ key: key1String, value: 'usable'}, { key: key2String, value: 'usable'}],
                                         'keyStatuses.entries() fails');

                    // forEach() should return both entries.
                    result = [];
                    mediaKeySession.keyStatuses.forEach(function(status, keyId) {
                        result.push({ key: arrayBufferAsString(keyId), value: status });
                    });
                    assert_object_equals(result,
                                         [{ key: key1String, value: 'usable'}, { key: key2String, value: 'usable'}],
                                         'keyStatuses.forEach() fails');

                    // has() and get() should return the expected values.
                    assert_true(mediaKeySession.keyStatuses.has(key1));
                    assert_true(mediaKeySession.keyStatuses.has(key2));
                    assert_equals(mediaKeySession.keyStatuses.get(key1), 'usable');
                    assert_equals(mediaKeySession.keyStatuses.get(key2), 'usable');

                    // Try some invalid keyIds.
                    var invalid1 = key1.subarray(0, key1.length - 1);
                    assert_false(mediaKeySession.keyStatuses.has(invalid1));
                    assert_equals(mediaKeySession.keyStatuses.get(invalid1), undefined);

                    var invalid2 = key1.subarray(1);
                    assert_false(mediaKeySession.keyStatuses.has(invalid2));
                    assert_equals(mediaKeySession.keyStatuses.get(invalid2), undefined);

                    var invalid3 = new Uint8Array(key1);
                    invalid3[0] += 1;
                    assert_false(mediaKeySession.keyStatuses.has(invalid3));
                    assert_equals(mediaKeySession.keyStatuses.get(invalid3), undefined);

                    var invalid4 = new Uint8Array(key1);
                    invalid4[invalid4.length - 1] -= 1;
                    assert_false(mediaKeySession.keyStatuses.has(invalid4));
                    assert_equals(mediaKeySession.keyStatuses.get(invalid4), undefined);

                    var invalid5 = new Uint8Array(key1.length + 1);
                    invalid5.set(key1, 1);  // First element will be 0.
                    assert_false(mediaKeySession.keyStatuses.has(invalid5));
                    assert_equals(mediaKeySession.keyStatuses.get(invalid5), undefined);

                    var invalid6 = new Uint8Array(key1.length + 1);
                    invalid6.set(key1, 0);  // Last element will be 0.
                    assert_false(mediaKeySession.keyStatuses.has(invalid6));
                    assert_equals(mediaKeySession.keyStatuses.get(invalid6), undefined);
                }

                function processKeyStatusesChange(event)
                {
                    if ( !closed )
                    {
                        // The first keystatuseschange (caused by update())
                        // should include both keys.
                        checkKeyStatusFor2Keys();

                        mediaKeySession.close().catch(function(error) {
                            forceTestFailureFromPromise(test, error);
                        });
                        closed = true;
                    }
                    else
                    {
                        // The second keystatuseschange (caused by close())
                        // should not have any keys, and the expiration time
                        // should be NaN.
                        assert_equals(mediaKeySession.keyStatuses.size, 0);
                        assert_true(isNaN(mediaKeySession.expiration));
                        test.done();
                    }
                }

                navigator.requestMediaKeySystemAccess('org.w3.clearkey', getSimpleConfiguration()).then(function(access) {
                    initDataType = access.getConfiguration().initDataTypes[0];
                    initData = getInitData(initDataType);
                    return access.createMediaKeys();
                }).then(function(mediaKeys) {
                    mediaKeySession = mediaKeys.createSession();

                    // There should be no keys defined yet.
                    assert_equals(mediaKeySession.keyStatuses.size, 0);

                    waitForEventAndRunStep('message', mediaKeySession, processMessage, test);
                    return mediaKeySession.generateRequest(initDataType, initData);
                }).catch(function(error) {
                    forceTestFailureFromPromise(test, error);
                });
            }, 'Verify MediaKeySession.keyStatuses.');
        </script>
    </body>
</html>
